package pool

import (
	"context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/keepalive"
	"time"
)

const (
	// DialTimeout 连接超时时长 5s
	DialTimeout = 5 * time.Second

	// BackoffMaxDelay 连接尝试失败后最长时延 3s
	BackoffMaxDelay = 3 * time.Second

	// KeepAliveTime 客户端没有活动时的保活时长，用于客户端ping服务端查看通信是否正常 10s
	KeepAliveTime = time.Duration(10) * time.Second

	// KeepAliveTimeout 客户端ping服务端没有回应时的超时时长 3s
	KeepAliveTimeout = time.Duration(3) * time.Second

	// MaxIdleTime
	MaxIdleTime = 3 * time.Second

	// InitialWindowSize 系统吞吐量we set it 1GB is to provide system's throughput.
	InitialWindowSize = 1 << 30

	// InitialConnWindowSize 系统吞吐量we set it 1GB is to provide system's throughput.
	InitialConnWindowSize = 1 << 30

	// MaxSendMsgSize gRPC请求的最大信息量，超过会报错
	MaxSendMsgSize = 4 << 30

	// MaxRecvMsgSize gRPC接收的最大信息量，超过会报错
	MaxRecvMsgSize = 4 << 30
)

// Options 当作创建grpc连接池的参数
type Options struct {
	// Dial 申请创建并确认连接
	Dial func(address string) (*grpc.ClientConn, error)
	// 最大空闲连接数
	MaxIdle int
	// 最小空闲连接数
	MinIdle int
	// 某时刻最大活跃连接数，为0时连接数无限
	MaxActive int
	// 单链接最大并发流数
	//MaxConcurrentStreams int
	// 达 MaxActive 时，如果为true，则继续使用池中的连接，即使用的是物理连接的逻辑连接，关闭连接时，对引用计数原子--即可，如果为false，则新建连接，关闭连接时还需要对连接进行真正的Close
	Reuse bool
}

var DefaultOptions = Options{
	Dial:      Dial,
	MaxIdle:   32,
	MinIdle:   8,
	MaxActive: 64,
	//MaxConcurrentStreams: 64,
	Reuse: true,
}

// Dial 返回一个带定义的grpc连接
func Dial(address string) (*grpc.ClientConn, error) {
	// 首先建立一个总体的超时
	ctx, cancel := context.WithTimeout(context.Background(), DialTimeout)
	defer cancel()
	return grpc.DialContext(ctx, address, grpc.WithInsecure(),
		grpc.WithBackoffMaxDelay(BackoffMaxDelay),
		grpc.WithInitialWindowSize(InitialWindowSize),
		grpc.WithInitialConnWindowSize(InitialConnWindowSize),
		grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(MaxSendMsgSize)),
		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(MaxRecvMsgSize)),
		grpc.WithKeepaliveParams(keepalive.ClientParameters{
			Time:                KeepAliveTime,
			Timeout:             KeepAliveTimeout,
			PermitWithoutStream: true,
		}))
}

func DialTest(address string) (*grpc.ClientConn, error) {
	ctx, cancel := context.WithTimeout(context.Background(), DialTimeout)
	defer cancel()
	return grpc.DialContext(ctx, address, grpc.WithInsecure())
}
